package com.example.mall.util;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.*;

public class CopyUtils {

    /**
     * 复制属性（空值不覆盖）
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        copyProperties(source, target, false);
    }

    /**
     * 复制属性（可控制空值行为）
     * @param source 源对象
     * @param target 目标对象
     * @param overwriteNull 是否用空值覆盖目标属性
     */
    public static void copyProperties(Object source, Object target, boolean overwriteNull) {
        if (source == null || target == null) {
            return;
        }

        // 获取源对象非空属性名
        String[] ignoreProperties = overwriteNull ? new String[0] : getNullPropertyNames(source);

        // 执行复制（Spring BeanUtils）
        BeanUtils.copyProperties(source, target, ignoreProperties);

        // 处理类型不匹配但可转换的属性
        handleTypeMismatchProperties(source, target, ignoreProperties);
    }

    /**
     * 获取对象中空值的属性名
     */
    private static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        return emptyNames.toArray(new String[0]);
    }

    /**
     * 处理类型不匹配但可转换的属性
     */
    private static void handleTypeMismatchProperties(Object source, Object target, String[] ignoreProperties) {
        BeanWrapper src = new BeanWrapperImpl(source);
        BeanWrapper trg = new BeanWrapperImpl(target);

        PropertyDescriptor[] sourcePds = src.getPropertyDescriptors();
        Set<String> ignoreSet = new HashSet<>(Arrays.asList(ignoreProperties));

        for (PropertyDescriptor sourcePd : sourcePds) {
            String propertyName = sourcePd.getName();

            // 跳过忽略的属性
            if (ignoreSet.contains(propertyName)) {
                continue;
            }

            // 只有目标对象有该属性时才处理
            if (trg.isReadableProperty(propertyName) && trg.isWritableProperty(propertyName)) {
                Object sourceValue = src.getPropertyValue(propertyName);
                Object targetValue = trg.getPropertyValue(propertyName);

                // 源值为空则跳过
                if (sourceValue == null) {
                    continue;
                }

                // 类型不匹配但可转换的情况
                if (targetValue != null && !sourceValue.getClass().equals(targetValue.getClass())) {
                    try {
                        Object convertedValue = convertValue(sourceValue, targetValue.getClass());
                        trg.setPropertyValue(propertyName, convertedValue);
                    } catch (Exception e) {
                        // 转换失败保持原值
                    }
                }
            }
        }
    }

    /**
     * 类型转换处理
     */
    private static Object convertValue(Object value, Class<?> targetType) {
        if (value == null) {
            return null;
        }

        // String -> 数字类型
        if (value instanceof String && Number.class.isAssignableFrom(targetType)) {
            String strValue = (String) value;
            if (strValue.isEmpty()) {
                return null;
            }
            if (targetType == Integer.class) {
                return Integer.parseInt(strValue);
            } else if (targetType == Long.class) {
                return Long.parseLong(strValue);
            } else if (targetType == Double.class) {
                return Double.parseDouble(strValue);
            }
        }

        // 数字 -> String
        if (value instanceof Number && targetType == String.class) {
            return value.toString();
        }

        // 其他简单类型转换可以在这里扩展

        // 默认返回原值（无法转换）
        return value;
    }
}
